/*  FSM template - A preprocessor template to construct finite state machines

    Copyright (C) 2013 Dushara Jayasinghe.

    fsm_template is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    fsm_template is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with fsm_template.  If not, see <http://www.gnu.org/licenses/>.
*/
/**
    \brief Template to build finite state machines (FSM)s

    This template uses preprocessor magic to build the FSM. It requires that
    three macros are defined before #including the template:

    1. STATE_LIST
    2. EVENT_LIST
    3. INITIAL_STATE
    4. RETURN_TYPE

    The following example illustrate how they should be defined:

    \code {.c}
    #define STATE_LIST(DEFINE_STATE)    DEFINE_STATE(state_name1)   \
                                        DEFINE_STATE(state_name2)   \
                                        DEFINE_STATE(state_name3)
    \endcode

    The argument for DEFINE_STATE is the name of the state.

    \code {.c}
    #define EVENT_LIST(DEFINE_EVENT, __fsm_internal_arg__)   \
        DEFINE_EVENT(event1,    { int arg; },                       __fsm_internal_arg__ )  \
        DEFINE_EVENT(event2,    { const char* string; int iarg; },  __fsm_internal_arg__ )  \
        DEFINE_EVENT(event3,    { int __dummy__; },                 __fsm_internal_arg__ )
    \endcode

    The DEFINE_EVENT macro arguments are:
        * Event name
        * Event payload (will construct a typedefed struct with the payload).
                        \note the event payload may require atleast 1 argument
                        (C doesn't allow empty structs) hence the __dummy__ in
                        event3).
        * __fsm_internal_arg__ - an opaque argument used to construct the state
                              machine action handlers (discussed below)

    This constructs a 2-dimensional matrix of events and states. The compiler
    then will require the definition of state/event action functions. In the
    above example, the state/event actions are as follows:

    \code {.c}
    static void state_name1_event1_action(const event1_t* payload, state_id_t* state);
    static void state_name2_event1_action(const event1_t* payload, state_id_t* state);
    static void state_name3_event1_action(const event1_t* payload, state_id_t* state);
    static void state_name1_event2_action(const event2_t* payload, state_id_t* state);
    static void state_name2_event2_action(const event2_t* payload, state_id_t* state);
    static void state_name3_event2_action(const event2_t* payload, state_id_t* state);
    static void state_name1_event3_action(const event3_t* payload, state_id_t* state);
    static void state_name2_event3_action(const event3_t* payload, state_id_t* state);
    static void state_name3_event3_action(const event3_t* payload, state_id_t* state);
    \endcode

    The parameters for the action functions are:
        * payload - A pointer to the event payload
        * state - A pointer to the state variable. The possible values for the current
                  example are:
                    state_name1_state,
                    state_name2_state and
                    state_name3_state
                  The action function can write to this location to cause a state
                  transition.

    The state machine can be invoked as follows:
    \code {.c}
    event1_event payload;
    payload.arg = 1234;

    post_event1_event(&payload);
    \endcode

*/
/* enumeration of states */
#define STATE_ENUM(name)    name##_state,
typedef enum
{
    STATE_LIST(STATE_ENUM)
} state_id_t;

/* event enumerations */
#define EVENT_ENUM(name, payload, state)   name##_event,
typedef enum
{
    EVENT_LIST(EVENT_ENUM, ~)
    event_id_count
} event_id_t;

/* Definitions of the event payload data */
#define TYPEDEF_EVENT_PAYLOAD(name, payload, state)    typedef struct payload name##_t ;
EVENT_LIST(TYPEDEF_EVENT_PAYLOAD, ~)

/* Definition of the event */
#define DECLARE_EVENT_PAYLOAD(name, payload, state)    const name##_t* name;
typedef struct
{
    event_id_t event_id;

    union
    {
        EVENT_LIST(DECLARE_EVENT_PAYLOAD, ~)
    } payload;
} event_t;

/* state handler function prototypes */
#define STATE_PROTOTYPE(name)    static RETURN_TYPE name##_handler(const event_t* event, state_id_t* current_state);
STATE_LIST(STATE_PROTOTYPE)

/* The state machine is a switch statement

    Each state within the state machine is a case statement that calls the
    individual state handler function. The following macro generates all the
    case statements using the STATE_LIST
*/
#define STATE_CASE(name)    case name##_state:                      \
                                rc = name##_handler(event, &state); \
                                break;

/*
    State machine implementation
*/
static RETURN_TYPE state_machine(const event_t* event)
{
    /*
    Declare variable to hold the current state and initialise it to
    the INITIAL_STATE
    */
    static state_id_t state = INITIAL_STATE;
    /* Initialise the return code to indicate unhandled state/event*/
    RETURN_TYPE rc;

    switch(state)
    {
        /* Dispatch the event to the current state handler */
        STATE_LIST(STATE_CASE)
        default:
            break;
    }

    return rc;
}

/* Creates the prototypes for the state/event action functions */
#define STATE_EVENT_PROTOTYPE(name, unused, state) \
    static RETURN_TYPE state##_##name##_action(const name##_t*, state_id_t*);
#define STATE_EVENT_PROTOTYPE_GENERATOR(name)   EVENT_LIST(STATE_EVENT_PROTOTYPE, name)
STATE_LIST(STATE_EVENT_PROTOTYPE_GENERATOR)

/* implementation of the state handler functions.

    The state handles is another switch statement that dispatches the event
    to the appropriate state/event action function which is implemented by
    the user.

    The return value of the action function is returned to the state machine.
 */
#define EVENT_CASE(name, unused, state_name)    case name##_event:              \
                                                rc = state_name##_##name##_action(event->payload.name, state); \
                                                break;

#define STATE_HANDLER(name)                                                         \
static RETURN_TYPE name##_handler(const event_t* event, state_id_t* state)          \
{                                                                                   \
    RETURN_TYPE rc;                                                                 \
    switch(event->event_id)                                                         \
    {                                                                               \
    EVENT_LIST(EVENT_CASE, name)                                                    \
    default:                                                                        \
        break;                                                                      \
    }                                                                               \
    return rc;                                                                      \
}
STATE_LIST(STATE_HANDLER)

/* Generate a set of functions to post events to the state machine

    These functions creates an event with the correct event id
*/
#define POST_EVENT_FUNCTION(name, __unused__, __fsm_internal_arg__)     \
static RETURN_TYPE post_##name##_event(const name##_t* payload)   \
{                                                                       \
    RETURN_TYPE rc;                                               \
    event_t event;                                                      \
    event.event_id = name##_event;                                      \
    event.payload.name = payload;                                       \
    rc = state_machine(&event);                                         \
    return rc;                                                          \
}
EVENT_LIST(POST_EVENT_FUNCTION, ~)
